package reduce.fz.com.reduce.util.http;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Process;
import android.os.SystemClock;
import android.util.LruCache;


import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;

import reduce.fz.com.reduce.util.DiskLruCache;
import reduce.fz.com.reduce.util.ReduceLog;


/**
 * 图片缓存处理
 * @author lipf
 */
public class BitmapLruCache implements ImageLoader.ImageCache {
    private LruCache<String, Bitmap> mMemoryCache;
    private DiskLruCache mDiskLruCache = null;
    /**
     * Create a cache with a given maximum size in bytes.
     */
    public BitmapLruCache(int maxSize , Context context , LruCache lruCache ,DiskLruCache diskLruCache ) {
        //内存缓存
        mMemoryCache = lruCache;
        mDiskLruCache = diskLruCache;

        ReadDiskQueue rdq = new ReadDiskQueue();
        rdq.start();
        WriteDiskQueue wdq = new WriteDiskQueue();
        wdq.start();
    }

    @Override
    public Bitmap getBitmap(String url) {
        Bitmap bitmap = null;
        synchronized (mMemoryCache) {
            bitmap = mMemoryCache.get(url);
            if (bitmap != null && !bitmap.isRecycled()) {//先从mem 里面获取
                return bitmap;
            } else { // 从disk 获取
                return null;
            }
        }
    }

    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        synchronized (mMemoryCache) {
            try{
                if(mDiskLruCache == null){
                    return;
                }
                String key = hashKeyForDisk(url);
                if( key == null ){
                    return;
                }
                if(!mDiskLruCache.isCached(key)){//没有放入到disk缓存的时候执行此操作
                    DiskCacheBitmap wcb = new DiskCacheBitmap(url,bitmap);
                    toCacheQueue.add(wcb);
                }
                mMemoryCache.put(url,bitmap);
            }catch (Exception e){
                ReduceLog.e(e);
            }
        }
    }

    /**
     * 从disk清除image cache
     * @param url
     * @return
     */
    public void removeDiskCache(String url){
        try{
            if(mDiskLruCache != null){
                String key = hashKeyForDisk(url);
                if( key != null ){
                    mDiskLruCache.remove(key);
                }
            }
        }catch (Throwable e){
            ReduceLog.e(e);
        }
    }

    @Override
    public boolean hasDiskCache(String url) {
        try{
            if(mDiskLruCache == null){
                return false;
            }
            String key = hashKeyForDisk(url);
            if( key == null ){
                return false;
            }
            return mDiskLruCache.isCached(key);
        }catch (Exception e){
            ReduceLog.e(e);
            return false;
        }
    }

    @Override
    public Bitmap getBitmapFromDisk(String url , ImageLoader.ImageContainer container) {
        cancelReads.remove(url);
        DiskCacheListener cacheLoader = new DiskCacheListener(url,container);
        mFileQueue.add(cacheLoader);
        return null;
    }

    @Override
    public Bitmap putBitmapFromDisk(String url) {
        return null;
    }

    @Override
    public void cancelBitmapFromDisk(String url) {
        cancelReads.put(url,true);
    }

    /**********disk 缓存******************/
    public String hashKeyForDisk(String key) {
        String cacheKey;
        try {
            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
            mDigest.update(key.getBytes());
            cacheKey = bytesToHexString(mDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(key.hashCode());
        }
        return cacheKey;
    }

    private String bytesToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(0xFF & bytes[i]);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }
    private static class DiskCacheListener {
        public String url;
        public ImageLoader.ImageContainer imageContainer;

        public DiskCacheListener(String url , ImageLoader.ImageContainer imageContainer){
            this.url = url;
            this.imageContainer = imageContainer;
        }
    }
    //每一个cache管理实例有一个唯一队列
    private final BlockingQueue<DiskCacheListener> mFileQueue =
            new LinkedBlockingQueue<DiskCacheListener>();
    private final ConcurrentHashMap<String,Boolean> cancelReads = new ConcurrentHashMap<String,Boolean>();
    /**
     * 读取sdk图片的线程，每个cache管理实例有两个线程同时处理
     */
    private class ReadDiskQueue extends Thread {
        private final BlockingQueue<DiskCacheListener> mQueue;
        private volatile boolean mQuit = false;

        public ReadDiskQueue(){
            mQueue = mFileQueue;
        }

        public void quit() {
            mQuit = true;
            interrupt();
        }

        @Override
        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            while (true) {
                long startTimeMs = SystemClock.elapsedRealtime();
                DiskCacheListener fileKey;
                try {
                    // Take a request from the queue.
                    fileKey = mQueue.take();
                } catch (InterruptedException e) {
                    // We may have been interrupted because it was time to quit.
                    if (mQuit) {
                        return;
                    }
                    continue;
                }
                //这里处理读取文件并且调用回调
                try{
                    String url = fileKey.url;
                    if(cancelReads.containsKey(url)){//已经取消了
                        cancelReads.remove(url);
                        continue;
                    }
                    String key = hashKeyForDisk(url);
                    DiskLruCache.Snapshot snapShot = null;
                    if( mDiskLruCache != null ){
                        snapShot = mDiskLruCache.get(key);
                    }
                    if (snapShot != null) {
                        InputStream is = snapShot.getInputStream(0);
                        Bitmap bitmap = null;
                        try{
                            bitmap = BitmapFactory.decodeStream(is);
                        } catch ( OutOfMemoryError err ){
                        }
                        if(bitmap!=null && !bitmap.isRecycled()){
                            ReduceLog.i("disk cache hit");
                            //放入内存以备下次使用
                            mMemoryCache.put(url, bitmap);
                            if(cancelReads.containsKey(url)){//已经取消了
                                cancelReads.remove(url);
                                continue;
                            }
                            fileKey.imageContainer.loadDiskResponse(url,bitmap);
                        }
                    } else {
                        //TODO 没从缓存加载到应该从网络加载
                    }
                } catch (Throwable e) {
                   ReduceLog.e(e);
                }
            }
        }
    }

    private static class DiskCacheBitmap {
        public String url;
        public Bitmap bitmap;

        public DiskCacheBitmap(String url , Bitmap bitmap){
            this.url = url;
            this.bitmap = bitmap;
        }
    }

    /*************下面是线程异步放bimap到disk***********/
    //需要放入disk的队列
    private final BlockingQueue<DiskCacheBitmap> toCacheQueue =
            new LinkedBlockingQueue<DiskCacheBitmap>();
    /**
     * 读取sdk图片的线程，每个cache管理实例有1个线程处理
     */
    private class WriteDiskQueue extends Thread {
        private final BlockingQueue<DiskCacheBitmap> mQueue;
        private volatile boolean mQuit = false;

        public WriteDiskQueue(){
            mQueue = toCacheQueue;
        }

        public void quit() {
            mQuit = true;
            interrupt();
        }

        @Override
        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            while (true) {
                long startTimeMs = SystemClock.elapsedRealtime();
                DiskCacheBitmap toCache;
                try {
                    // Take a request from the queue.
                    toCache = mQueue.take();
                } catch (InterruptedException e) {
                    // We may have been interrupted because it was time to quit.
                    if (mQuit) {
                        return;
                    }
                    continue;
                }
                //这里处理读取文件并且调用回调
                try{
                    String url = toCache.url;
                    String key = hashKeyForDisk(url);
                    if(mDiskLruCache != null && !mDiskLruCache.isCached(key)){//disk 没有缓存的时候才放进disk
                        DiskLruCache.Editor editor = mDiskLruCache.edit(key);
                        if (editor != null) {
                            OutputStream outputStream = editor.newOutputStream(0);
                            toCache.bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
                            editor.commit();
                        }
                        mDiskLruCache.flush();
                    }
                } catch (Exception e) {
                    ReduceLog.e(e);
                }
            }
        }
    }
}
